package ilearn.job.ilearnjobgrayversionjava17.buss.config;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;

@Log4j2
public class GrayVersionLoadBlancerClient implements ReactorServiceInstanceLoadBalancer {

    final String serviceId;
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierObjectProvider;

    private VersionHandler versionHandler;

    public GrayVersionLoadBlancerClient(String serviceId,ObjectProvider<ServiceInstanceListSupplier>  provider) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierObjectProvider = provider;
        versionHandler = new HttpHeaderVersionHandler();
        versionHandler.next(new ParmVersionHandler())
                .next(new PathVersionHandler())
                .next(new CookieVersionHandler());
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        log.info("进入自定义负载均衡器，当前请求内容:{}",request.getContext());
        String versionTarget = getRequestVersion(request);
        ServiceInstanceListSupplier serviceInstanceListSupplier =
                serviceInstanceListSupplierObjectProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);

        return serviceInstanceListSupplier.get().next().map(
                list->getInstanceResponse(serviceInstanceListSupplier,list,versionTarget));
    }

    private Response<ServiceInstance> getInstanceResponse(ServiceInstanceListSupplier supplier,
                                                          List<ServiceInstance> list, String version) {
        log.info("待过滤服务实例:{}",list);
        List<ServiceInstance> matchList ;
        if(StringUtils.hasText(version)){
            matchList = list.stream().filter(
                    l-> Objects.equals(l.getMetadata().get("version"),version)).toList();
        }else {
            log.info("请求版本号为空，随机路由到无版本服务");
            matchList = list.stream().filter(l->Objects.isNull(l.getMetadata().get("version"))).toList();
        }

        if (matchList.isEmpty()){
            log.error("没有符合【{}】版本的实例",version);
            return new EmptyResponse();
        }

        int index = ThreadLocalRandom.current().nextInt(matchList.size());
        ServiceInstance serviceInstance = matchList.get(index);
        log.info("获取到服务实例:{}",serviceInstance);
        Response<ServiceInstance> serviceInstanceResponse = new DefaultResponse(serviceInstance);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private String getRequestVersion(Request request){
        String version = versionHandler.getVersion(request);
        log.info("当前请求版本号：{}",version);
        return version;
    }

    abstract class VersionHandler{
        private VersionHandler next;

        VersionHandler next(VersionHandler versionHandler){
            return (next = versionHandler);
        }

        public String getVersion(Request request){
            String version = fromVersion(request);
            if (StringUtils.hasText(version)){
                return version;
            }else if(next != null){
                return next.getVersion(request);
            }else {
                return null;
            }
        }

        abstract String fromVersion(Request request);

    }

    class HttpHeaderVersionHandler extends VersionHandler{

        @Override
        public String fromVersion(Request request) {
            String version = null;
            if(request instanceof HttpServletRequest){
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                version = httpServletRequest.getHeader("version");
            }else {
                RequestDataContext requestDataContext = (RequestDataContext) request.getContext();
                List<String> versionFromheader = requestDataContext.getClientRequest().getHeaders().get("version");
                if(versionFromheader != null && !versionFromheader.isEmpty()){
                    version = versionFromheader.get(0);
                }
            }
            log.info("HTTP请求头获取版本号:{}",version);
            return version;
        }
    }

    class ParmVersionHandler extends VersionHandler{

        @Override
        public String fromVersion(Request request) {
            String version = null;
            if(request instanceof HttpServletRequest){
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                version = httpServletRequest.getParameter("version");
            }else {
                RequestDataContext requestDataContext = (RequestDataContext) request.getContext();
                String path = requestDataContext.getClientRequest().getUrl().toString();
                int index = path.lastIndexOf("?");
                if (index > 0 && index < path.length() - "version=1".length()) {
                    String[] parms = path.substring(index).split("&");
                    for (String parm : parms) {
                        if (parm.startsWith("version=")) {
                            version = parm.substring("version=".length());
                            break;
                        }
                    }
                }
            }
            log.info("HTTP请求参数获取版本号:{}",version);
            return version;
        }
    }

    class PathVersionHandler extends VersionHandler{

        @Override
        public String fromVersion(Request request) {
            String version = null;
            String path;
            if(request instanceof HttpServletRequest){
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                path = httpServletRequest.getContextPath();
            }else {
                RequestDataContext requestDataContext = (RequestDataContext) request.getContext();
                path = requestDataContext.getClientRequest().getUrl().toString();
            }

            String[] pathParms = path.split("/");
            for (String var:pathParms){
                if (var.startsWith("v=") && var.length() > "v=".length()){
                    version = var.substring(2);
                    break;
                }
            }
            log.info("HTTP请求路径获取版本号:{}",version);
            return version;
        }
    }

    class CookieVersionHandler extends VersionHandler{

        @Override
        public String fromVersion(Request request) {
            String version = null;
            if(request instanceof HttpServletRequest){
                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
                Cookie[] cookies = httpServletRequest.getCookies();
                for (Cookie cookie:cookies){
                    if ("version".equals(cookie.getName())){
                        version = cookie.getValue();
                        break;
                    }
                }
            }else {
                RequestDataContext requestDataContext = (RequestDataContext) request.getContext();
                MultiValueMap<String, String> cookies = requestDataContext.getClientRequest().getCookies();
                log.info("MultiValueMap:{}",cookies);
                if (cookies != null){
                    version = cookies.getFirst("version");
                }
                if (!StringUtils.hasText(version)){
                    List<String> list = requestDataContext.getClientRequest().getHeaders().get("Cookie");
                    for (String cookie : list) {
                        if (cookie.startsWith("version=")) {
                            version = cookie.substring("version=".length());
                            break;
                        }
                    }
                }
            }
            log.info("HTTP请求Cookie获取版本号:{}",version);
            return version;
        }
    }
}
